<template>
  <div>
    <div class="container">
      <div ref="initArea" class="init-area" v-if="!isMounted">
        <slot :data="data ? data[0] : {}"></slot>
      </div>
      <div class="content-container" v-else>
        <div class="layer-container" ref="layer" :style="layerContainerStyle" @mousedown="onMouseDown" @mouseup="onMouseUp" @mousemove="onMouseMove" @mouseout="onMouseOut">
          <div class="item-container" :style="itemContainerStyle">
            <div class="item-style" v-for="(itemStyle, index) in dataStyle" :key="index" :style="itemStyle">
              <slot :data="data[index]"></slot>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  function getAngleChange(radius, length) {
    const sinVal = length / 2 / radius;
    return Math.asin(sinVal) / Math.PI * 360;
  }

  function createPoint(x, y, z, horDiff, angleChange) {
    return {
      x: x,
      y: y,
      z: z,
      afterY: y,
      afterZ: z,
      horDiff: horDiff,
      angleChange: angleChange,
      angle: 0,
      rotate() {
        this.angle += angleChange;
        this.angle = this.angle % 360;
        const rx = this.angle * Math.PI / 180;
        const orgY = this.y;
        const orgZ = this.z;
        const cos = Math.cos(rx);
        const sin = Math.sin(rx);
        this.afterY = cos * orgY - sin * orgZ;
        this.afterZ = cos * orgZ + sin * orgY;
        this.x += this.horDiff;
        // console.log('y' + this.y + ',angle: ' + this.angle);
      },
      getTransform() {
        return 'transform: translate3d(' + this.x + 'px, ' + this.afterY + 'px, ' + this.afterZ + 'px)' + ' rotate3d(1, 0, 0, ' + (this.angle) + 'deg)';
      }
    }
  }

  export default {
    name: "animate-helix",
    isMouseDown: false,
    data () {
      return {
        data: [],
        dataStyle: [],
        layerContainerStyle: '',
        itemContainerStyle: '',

        touchX: 0,
        touchY: 0,
        touchDeg: 0,
        curDeg: 0,

        perimeter: 0,
        contentWidth: 0,
        contentHeight: 0,
        radius: 0,
        itemWidth: 0,
        itemHeight: 0,
        itemSpacing: 0,
        rowItemCount: 0,
        horDiff: 0,
        isMounted: false,
      }
    },
    methods: {
      test(deg, period) {
        var vm = this;
        setTimeout(function() {
          vm.rotateItemContainer(deg);
          vm.test(deg + 15, period);
        }, period);
      },

      rotateItemContainer(deg) {
        deg %= 360;
        this.curDeg = deg > 0 ? deg : 360 + deg;
        var translateY = 0;
        var translateZ = 0;
        // console.log(deg);
        if(this.curDeg < 180) {
          translateY = this.radius - this.contentHeight * this.curDeg / 180;
        }
        else {
          translateY = -this.radius + this.contentHeight * (this.curDeg / 180 - 1);
        }

        if(this.curDeg <= 90) {
          translateZ = (this.curDeg) / 90 * this.radius;
        }
        else if(this.curDeg <= 180) {
          translateZ = this.radius - (this.curDeg - 90) / 90 * this.radius;
        }
        else if(this.curDeg <= 270) {
          translateZ = -(this.curDeg - 180) / 90 * this.radius;
        }
        else {
          translateZ = -this.radius + (this.curDeg - 270) / 90 * this.radius;
        }


        this.itemContainerStyle = 'width: ' + this.contentWidth + 'px; height: ' + this.contentHeight + 'px; ' +
          'transform-style: preserve-3d; transform: translate3d(0, ' + translateY + 'px, ' + translateZ + 'px) rotateX(' + this.curDeg + 'deg);';
      },

      judgeInLayer(x, y) {
        const layerElem = this.$refs.layer;
        if(layerElem) {
          const bound = layerElem.getBoundingClientRect();
          if(x >= bound.left && x <= bound.right && y >= bound.top && y <= bound.bottom) {
            return true;
          }
        }
        return false;
      },

      onMouseDown (e) {
        this.isMouseDown = true;
        this.touchX = e.clientX;
        this.touchY = e.clientY;
        this.touchDeg = this.curDeg;
      },

      onMouseMove(e) {
        if(this.isMouseDown) {
          var touchY = e.clientY;
          var diffY = touchY - this.touchY;
          this.rotateItemContainer(this.touchDeg + diffY / 10);
        }
      },

      onMouseUp () {
        this.isMouseDown = false;
      },

      onMouseOut(e) {
        if(this.isMouseDown) {
          if(!this.judgeInLayer(e.clientX, e.clientY)) {
            this.isMouseDown = false;
          }
        }
      }
    },
    props: {
      height: Number,
      spacing: Number,
      initData: Array,
      newData: Array,
    },
    mounted () {
      if(this.initData == null && this.newData == null) {
        return;
      }
      const totalCount = (this.initData ? this.initData.length : 0) + (this.newData ? this.newData.length : 0);
      if(totalCount === 0) {
        return;
      }
      this.contentHeight = this.height ? this.height : 300;
      this.radius = this.contentHeight / 2;
      const itemSpacing = this.spacing ? this.spacing : 10;
      this.perimeter = this.contentHeight * Math.PI;
      this.itemWidth = this.$refs.initArea.offsetWidth;
      this.itemHeight = this.$refs.initArea.offsetHeight;
      this.data = this.initData;

      this.rowItemCount = Math.floor(this.perimeter / (itemSpacing + this.itemHeight));
      this.itemSpacing = (this.perimeter - (this.itemHeight * this.rowItemCount)) / this.rowItemCount;
      this.horDiff = (this.itemWidth + this.itemSpacing) / this.rowItemCount;
      this.contentWidth = this.itemWidth + totalCount * this.horDiff;

      this.layerContainerStyle = 'width: ' + this.contentWidth + 'px; height: ' + this.contentHeight + 'px; ' +
        'perspective: ' + this.perimeter + 'px';

      this.rotateItemContainer(0);

      var defaultStyle = 'width: ' + this.itemWidth + 'px; height: ' + this.itemHeight + 'px; ';
      const angleChange = getAngleChange(this.contentHeight / 2, this.itemHeight) + getAngleChange(this.contentHeight / 2, this.itemSpacing);
      const point = createPoint(0, 0, this.contentHeight / 2, this.horDiff, angleChange);

      if(this.initData != null) {
        const length = this.initData.length;
        for(var i = 0 ; i < length ; i ++) {
          this.dataStyle.push(defaultStyle + point.getTransform());
          point.rotate();
        }
      }

      this.isMounted = true;
      // this.test(0, 30);
    }
  }
</script>

<style scoped>

  .init-area {
    opacity: 0;
    display: inline-block;
  }


  .container {
    display: inline-block;
  }

  .content-container {
    display: inline-block;
    background-color: black;
  }

  .layer-container {
    display: inline-block;
  }

  .item-container {
    display: inline-block;
    background-color: blue;
  }

  .item-style {
    position: absolute;
    background-color: red;
    transform-style: preserve-3d;
    opacity: 0.5;
    transition: all 1s;
  }

  .item-style:hover {
    opacity: 1;
  }
</style>
